/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.controller.evaluation.planning;

import cn.ac.nercis.pes.common.constant.project.SpecialType;
import cn.ac.nercis.pes.community.component.dialog.*;
import cn.ac.nercis.pes.community.constant.AppConstants;
import cn.ac.nercis.pes.community.controller.base.GlobalEventHandle;
import cn.ac.nercis.pes.community.model.vo.MenuVO;
import cn.ac.nercis.pes.community.utils.*;
import cn.ac.nercis.pes.community.views.evaluation.planning.ContentConfirmView;
import cn.ac.nercis.pes.model.dto.common.TreeNode;
import cn.ac.nercis.pes.model.dto.evaluation.planning.RelatedTargetDTO;
import cn.ac.nercis.pes.model.dto.knowledge.KmSimpleTargetDTO;
import cn.ac.nercis.pes.model.dto.target.UnitSpecialTargetDTO;
import cn.ac.nercis.pes.model.dto.target.UnitTargetDTO;
import cn.ac.nercis.pes.service.evaluation.EvaluationRangeService;
import cn.ac.nercis.pes.service.project.SystemTargetService;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.net.URL;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
 * 资产对象关联指标管理控制器
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
public class RelatedTargetController implements Initializable, GlobalEventHandle {

    @FXML
    private StackPane page;
    @FXML
    private Button saveButton;
    @FXML
    private Button backButton;
    @FXML
    private Label currentAssetLbl;
    @FXML
    private VBox targetBox;

    private final SystemTargetService systemTargetService;
    private final EvaluationRangeService evaluationRangeService;
    private final ExecutorService executor;

    private MenuVO currentAssetNode;
    private String currentSystemId;
    private KmSimpleTargetDTO currentLayerTarget;

    public RelatedTargetController() {
        this.systemTargetService = SpringUtils.getBean(SystemTargetService.class);
        this.evaluationRangeService = SpringUtils.getBean(EvaluationRangeService.class);
        this.executor = SpringUtils.getBean(ExecutorService.class);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        saveButton.setGraphic(IconUtils.getGlyph("save-button-icon","btn-icon"));
        backButton.setGraphic(IconUtils.getGlyph("back-button-icon","btn-icon"));
        GuiStore.applyQuickSave(this.page,this::onGlobalSave);
    }

    public void initializeData(String systemId,MenuVO assetNode){
        LogUtils.info(log, "资产对象关联指标信息...");
        this.currentSystemId = systemId;
        this.currentAssetNode = assetNode;
        ControlUtils.setLabelTextByLimit(this.currentAssetLbl,assetNode.getTitle(), AppConstants.MAX_SHOW_TEXT_LENGTH);
        this.loadTargetControl();
    }

    @Override
    public void onGlobalSave() {
        LogUtils.info(log, "执行快捷保存操作...");
        onSave();
    }

    @FXML
    public void onSave(){
        LogUtils.info(log, "执行保存操作...");
        List<String> selectedTargetId = this.getSelectedRelatedTarget();
        if(CollectionUtils.isEmpty(selectedTargetId)){
            MessageDialog.builder()
                    .withContainer(this.page)
                    .withType(MessageType.WARN)
                    .withMessage("必需选择一个要关联到的测评单元指标")
                    .build().show();
            return;
        }
        checkRelatedTarget(RelatedTargetDTO.builder()
                .systemId(this.currentSystemId)
                .assetId(this.currentAssetNode.getCode())
                .unitTargetIds(selectedTargetId)
                .build());
    }

    @FXML
    public void onBack(){
        LogUtils.info(log, "执行返回操作...");
        var view = AppStore.getEvaluationView(this.currentSystemId, ContentConfirmView.class);
        if(view.getController() instanceof ContentConfirmController controller){
            controller.gotoEvaluationMethod(currentAssetNode);
        }
    }

    private void checkRelatedTarget(RelatedTargetDTO targetDTO) {
        LogUtils.info(log, "检查关联指标信息...");
        LogUtils.record(log, "请求数据:{}", targetDTO);
        CompletableFuture.supplyAsync(() -> this.evaluationRangeService.checkRelatedTarget(targetDTO),this.executor)
                .thenAccept(res -> Platform.runLater(() -> {
                    if (res.isSuccess()) {
                        saveData(targetDTO);
                    } else {
                        AlertDialog alertDialog = AlertDialog.builder()
                                .withMode(AlertMode.CONFIRM)
                                .withType(AlertType.WARN)
                                .withTitle("操作提示")
                                .withContent(res.getMsg())
                                .withContainer(this.page)
                                .build();
                        alertDialog.show((confirm) -> {
                            if (confirm) {
                                saveData(targetDTO);
                            } else {
                                this.loadTargetControl();
                            }
                        });
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("检查关联指标信息出错!", ex);
                    return null;
                });
    }

    private void saveData(RelatedTargetDTO targetDTO) {
        LogUtils.info(log, "保存关联指标信息...");
        LogUtils.record(log, "请求数据:{}", targetDTO);
        CompletableFuture.supplyAsync(() -> this.evaluationRangeService.saveRelatedTarget(targetDTO),this.executor)
                .thenAccept(res -> Platform.runLater(() -> {
                    if (res.isSuccess()) {
                        MessageDialog.builder()
                                .withContainer(this.page)
                                .withType(MessageType.SUCCESS).withMessage("保存成功")
                                .build().show();
                        onBack();
                    } else {
                        MessageDialog.builder()
                                .withContainer(this.page)
                                .withType(MessageType.FAIL).withMessage(res.getMsg())
                                .build().show();
                    }
                }))
                .exceptionally((ex) -> {
                    log.error("保存关联指标信息出错!", ex);
                    return null;
                });
    }

    private void loadTargetControl() {
        LogUtils.info(log, "获取单元指标树节点数据...");
        LogUtils.record(log, "请求数据:{}", this.currentSystemId);
        CompletableFuture.supplyAsync(() -> this.systemTargetService.findUnitTargetTree(this.currentSystemId,this.currentAssetNode.getCode()),this.executor)
                .thenAccept(res -> Platform.runLater(() -> initLayerTargetControl(res)))
                .exceptionally((ex) -> {
                    log.error("获取单元指标树节点数据出错!", ex);
                    return null;
                });
    }

    private void loadRelatedTarget() {
        LogUtils.info(log, "获取关联指标信息...");
        LogUtils.record(log, "请求数据，systemId：{},data:{}", this.currentSystemId, currentAssetNode.getCode());
        CompletableFuture.supplyAsync(() -> this.evaluationRangeService.getRelatedTarget(this.currentSystemId,
                        currentAssetNode.getCode()),this.executor)
                .thenAccept(res -> Platform.runLater(() -> initSelectedRelatedTarget(res)))
                .exceptionally((ex) -> {
                    log.error("获取关联指标信息出错!", ex);
                    return null;
                });
    }

    private void initLayerTargetControl(List<TreeNode> layerTargets){
        targetBox.getChildren().clear();
        layerTargets.forEach(layerTargetNode->{
            VBox ltBox = new VBox();
            ltBox.getStyleClass().add("content-block");
            ltBox.setPadding(new Insets(0,0,15,0));
            HBox ltTitleBox = new HBox();
            ltTitleBox.getStyleClass().add("block-title");
            Label ltTitleLabel = new Label(layerTargetNode.getName());
            ltTitleLabel.getStyleClass().add("label");
            ltTitleBox.getChildren().add(ltTitleLabel);
            ltBox.getChildren().add(ltTitleBox);

            this.initUnitTargetControl(ltBox, layerTargetNode.getChildren());
            targetBox.getChildren().add(ltBox);
        });
        loadRelatedTarget();
    }

    private void initSelectedRelatedTarget(RelatedTargetDTO relatedTarget){
        targetBox.getChildren().stream()
                .filter(n->n instanceof VBox)
                .flatMap(n->((VBox) n).getChildren().stream())
                .filter(n->n instanceof FlowPane)
                .flatMap(n->((FlowPane) n).getChildren().stream())
                .filter(n->n instanceof CheckBox)
                .forEach(cbx->{
                    CheckBox targetCbx = (CheckBox) cbx;
                    String unitTargetId = String.valueOf(targetCbx.getUserData());
                    targetCbx.setSelected(relatedTarget.getUnitTargetIds().stream()
                            .anyMatch(id-> StringUtils.equals(id,unitTargetId)));
                });
    }

    private List<String> getSelectedRelatedTarget(){
        return targetBox.getChildren().stream()
                .filter(n->n instanceof VBox)
                .flatMap(n -> ((VBox) n).getChildren().stream())
                .filter(n -> n instanceof FlowPane)
                .flatMap(n -> ((FlowPane) n).getChildren().stream())
                .filter(n -> n instanceof CheckBox)
                .map(n -> (CheckBox) n)
                .filter(CheckBox::isSelected)
                .map(cbx -> String.valueOf(cbx.getUserData()))
                .collect(Collectors.toList());
    }

    private void initUnitTargetControl(VBox box, List<TreeNode> unitTargets) {
        FlowPane unitPane = new FlowPane();
        unitPane.setHgap(20);
        unitPane.setVgap(10);
        unitPane.setPadding(new Insets(20, 40, 0, 40));
        VBox.setVgrow(unitPane, Priority.SOMETIMES);
        var standTargets = unitTargets.stream().filter(tn->tn.getData() instanceof UnitTargetDTO).toList();
        var specialTargets = unitTargets.stream().filter(tn->tn.getData() instanceof UnitSpecialTargetDTO).toList();
        standTargets.forEach(unitTargetNode -> {
            CheckBox unitCbx = new CheckBox();
            unitCbx.setText(unitTargetNode.getName());
            unitCbx.setUserData(unitTargetNode.getId());
            unitCbx.setContentDisplay(ContentDisplay.RIGHT);
            specialTargets.stream().filter(st->StringUtils.equals(st.getName(),unitTargetNode.getName()))
                    .findFirst()
                    .ifPresent(st->unitCbx.setGraphic(IconUtils.getGlyph("target-up-icon","special-target-icon")));
            unitPane.getChildren().add(unitCbx);
        });
        specialTargets.forEach(specialTargetNode->{
            var specialTargetData = (UnitSpecialTargetDTO)specialTargetNode.getData();
            if(standTargets.stream().anyMatch(st->StringUtils.equals(st.getName(),specialTargetNode.getName()))
                    || Objects.isNull(specialTargetData)){
                return;
            }
            CheckBox unitCbx = new CheckBox();
            unitCbx.setText(specialTargetNode.getName());
            unitCbx.setUserData(specialTargetNode.getId());
            unitCbx.setContentDisplay(ContentDisplay.RIGHT);
            if(specialTargetData.getSpecialType()== SpecialType.UPLEVEL){
                unitCbx.setGraphic(IconUtils.getGlyph("target-ref-icon","special-target-icon"));
            }else{
                unitCbx.setGraphic(IconUtils.getGlyph("target-custom-icon","special-target-icon"));
            }
            unitPane.getChildren().add(unitCbx);
        });
        box.getChildren().add(unitPane);
    }

}